package api

import (
	"github.com/gin-gonic/gin"
	"github.com/thoas/go-funk"
	"go.uber.org/zap"
	"seat-service/initialization"
	"seat-service/model"
	"seat-service/model/dto"
	"seat-service/response"
	service "seat-service/service/impl"
	"strconv"
	"strings"
)

var menuService service.MenuService

type MenuApi struct {
}

func intToBool(value int) bool {
	return value != 0
}

func (*MenuApi) Form(c *gin.Context) {
	par := c.Param("id")
	id, err := strconv.Atoi(par)
	if err != nil {
		resp.Fail(c, response.CodeParamError, nil)
		return
	}
	menus, err := menuService.GetMenus(uint(id))
	if err != nil {
		initialization.SeatLogger.Error("menuService.GetMenus(uint(id)) failed", zap.Error(err))
		resp.Fail(c, response.CodeServerBusy, nil)
		return
	}
	data := converToFormResponse(&menus)
	resp.Success(c, response.CodeSuccess, data)
}

var allwayMap = map[int]string{0: "否", 1: "是"}

func converToFormResponse(menu *model.Menu) dto.FormResponse {
	node := dto.FormResponse{
		ID:            menu.ID,
		ParentId:      menu.ParentID,
		Name:          menu.Name,
		Type:          typeMap[menu.Type],
		Path:          menu.Path,
		Component:     menu.Component,
		Sort:          menu.Sort,
		Visible:       menu.Visible,
		Icon:          menu.Icon,
		Redirect:      menu.Redirect,
		Perm:          menu.Perm,
		AlwaysShow:    allwayMap[menu.AlwaysShow],
		RequestUrl:    menu.RequestUrl,
		RequestMethod: menu.RequestMethod,
	}
	return node
}

func (m *MenuApi) Options(c *gin.Context) {
	menus := m.GetAllMenus()
	//获取当前分配权限的用户的code码
	par := c.Query("role_code")
	roleCode, err := strconv.Atoi(par)
	if err != nil {
		resp.Fail(c, response.CodeParamError, nil)
		return
	}
	//根据code码查询用户的角色
	roleName, err := casbinService.GetRoleName(roleCode)
	if err != nil {
		resp.Fail(c, response.CodeServerBusy, nil)
		return
	}
	//将roleName 结构体中的V0字段提取出来转化为字符串
	roleNames := funk.Map(roleName, func(role model.CasbinRuleExtended) string {
		return role.V1
	}).([]string)
	result := strings.Join(roleNames, " ")
	data := []dto.OptionsResponse{}
	for i := range menus {
		data = append(data, converToMenNode(menus[i], result))
	}
	resp.Success(c, response.CodeSuccess, data)
	return
}

func converToMenNode(menu *model.Menu, roleName string) dto.OptionsResponse {
	//查看当前登录的用户对菜单的权限
	iscontrol, _ := casbinService.CheckControl(roleName, int(menu.ID))
	var numTemp uint8
	if iscontrol {
		numTemp = 1
	}
	node := dto.OptionsResponse{
		Value:     menu.ID,
		Label:     menu.Name,
		IsControl: numTemp,
	}
	for i := range menu.Children {
		child := menu.Children[i]
		node.Children = append(node.Children, converToMenNode(&child, roleName))
	}
	return node
}

func (*MenuApi) GetRouter(c *gin.Context) {
	menus, err := menuService.GetRouter()
	if err != nil {
		initialization.SeatLogger.Error("menuService.GetRouter() failed", zap.Error(err))
		resp.Fail(c, response.CodeServerBusy, nil)
		return
	}
	data := []dto.DynamicRoutesResponse1{}
	// 格式转换
	for _, menu := range menus {
		role := strings.Split(menu.Roles, ",")
		for i := range role {
			role[i] = strings.ToUpper(role[i])
		}
		name := ""
		if menu.Path[0] == '/' {
			name = menu.Path
		} else {
			name = strings.Title(menu.Path)
		}
		tmp := dto.DynamicRoutesResponse1{
			Path:      menu.Path,
			Component: menu.Component,
			Name:      name,
			Redirect:  menu.Redirect,
			Meta: dto.RouteContentResponse{
				Hidden:    !intToBool(menu.Visible),
				Icon:      menu.Icon,
				Title:     menu.Name,
				Roles:     role,
				KeepAlive: intToBool(menu.KeepAlive),
			},
		}
		for _, child := range menu.Children {
			c := dto.DynamicRoutesResponse1{
				Path:      child.Path,
				Component: child.Component,
				Name:      strings.Title(child.Path),
				Meta: dto.RouteContentResponse{
					Hidden:    !intToBool(child.Visible),
					Icon:      child.Icon,
					Title:     child.Name,
					Roles:     role,
					KeepAlive: intToBool(child.KeepAlive),
				},
			}
			tmp.Children = append(tmp.Children, c)
		}
		data = append(data, tmp)
	}
	resp.Success(c, response.CodeSuccess, data)
}

// GetMenus 获取菜单
func (m *MenuApi) GetMenus(c *gin.Context) {
	par := c.Query("keywords")
	dbMenus := m.GetAllMenus()
	var menus []*model.Menu
	var recursion func()
	recursion = func() {
		cur := dbMenus
		for len(cur) != 0 {
			child := []*model.Menu{}
			for _, menu := range cur {
				if strings.Contains(menu.Name, par) {
					menus = append(menus, menu)
					continue
				}
				if len(menu.Children) != 0 {
					for i := range menu.Children {
						child = append(child, &menu.Children[i])
					}
				}
			}
			cur = child
		}
	}
	if par != "" {
		recursion()
	} else {
		menus = dbMenus
	}
	data := []dto.MenusResponse{}
	for i := range menus {
		data = append(data, converToMenusResponse(menus[i]))
	}
	resp.Success(c, response.CodeSuccess, data)
}

var typeMap = map[int]string{1: "MENU", 2: "CATALOG", 3: "EXTLINK", 4: "BUTTON"}

func converToMenusResponse(menu *model.Menu) dto.MenusResponse {
	node := dto.MenusResponse{
		ID:        menu.ID,
		ParentId:  menu.ParentID,
		Name:      menu.Name,
		Type:      typeMap[menu.Type],
		Path:      menu.Path,
		Component: menu.Component,
		Sort:      menu.Sort,
		Visible:   menu.Visible,
		Icon:      menu.Icon,
		Redirect:  menu.Redirect,
		Perm:      menu.Perm,
	}
	for i := range menu.Children {
		child := menu.Children[i]
		node.Children = append(node.Children, converToMenusResponse(&child))
	}
	return node
}

const (
	NULL    = iota // null
	MENU           // 菜单
	CATALOG        // 目录
	EXTLINK        // 外链
	BUTTON         // 按钮
)

// SaveMenu 保存菜单
func (*MenuApi) SaveMenu(c *gin.Context) {
	par := &model.Menu{}
	if err := c.ShouldBind(par); err != nil {
		initialization.SeatLogger.Error("c.ShouldBind(par) failed", zap.Error(err))
		resp.Fail(c, response.CodeParamError, nil)
		return
	}
	if par.Type == CATALOG { // 如果是目录
		if par.ParentID == 0 && par.Path[0] != '/' {
			par.Path = "/" + par.Path // 一级目录需以 / 开头
		}
		par.Component = "Layout"
	} else if par.Type == EXTLINK { // 如果是外链
		par.Component = ""
	}
	parent, _ := menuService.GetMenus(par.ParentID)
	treeParent := strconv.Itoa(int(parent.ID)) + parent.TreePath
	par.TreePath = treeParent
	if par.ID != 0 {
		//存在执行修改
		if err := menuService.UpdatesMenu(par); err != nil {
			initialization.SeatLogger.Error("menuService.UpdatesMenu(par) failed", zap.Error(err))
			resp.Fail(c, response.CodeServerBusy, nil)
			return
		}
	} else {
		//不存在新增
		if err := menuService.SaveMenu(par); err != nil {
			initialization.SeatLogger.Error("menuService.AddMenu(par) failed", zap.Error(err))
			resp.Fail(c, response.CodeServerBusy, nil)
			return
		}
	}

	resp.Success(c, response.CodeSuccess, nil)
}

// DeleteMenu 删除菜单
func (*MenuApi) DeleteMenu(c *gin.Context) {
	par := c.Query("id")
	id, err := strconv.Atoi(par)
	if err != nil {
		initialization.SeatLogger.Error("strconv.Atoi(par) failed", zap.Error(err))
		resp.Fail(c, response.CodeParamError, nil)
		return
	}
	if err := menuService.DeleteMenu(id); err != nil {
		initialization.SeatLogger.Error("menuService.DeleteMenu(id) failed", zap.Error(err))
		resp.Fail(c, response.CodeServerBusy, nil)
		return
	}
	resp.Success(c, response.CodeSuccess, nil)
}

// GetAllMenus 获取所有菜单
func (*MenuApi) GetAllMenus() (menus []*model.Menu) {
	// 获取一级菜单
	menus, _ = menuService.GetFirstMenus()
	// TODO 预加载递归调用 查询菜单下的子菜单及其子菜单
	for i := range menus {
		menuService.PreloadChildren(menus[i])
	}
	return menus
}
